package dse.modules.eln;

import java.util.Map;
import java.util.Random;

import testing.EarthLikeNetworkImagingUtil;
import testing.TestHelper;
import dse.DseException;
import dse.Network;
import dse.ExperimentConfiguration;
//import dse.Node;
import dse.modules.ModuleUtil;
import dse.modules.NetworkTopologyGenerator;

class EarthLikeArea
{
	private double xStart;
	private double yStart;
	private int width;
	private int height;
	private int nodeNumber;
	private int poles;
	private double minPull;
	private double[] poleXs;
	private double[] poleYs;
	private Random random;
	private int currentIndex;
	
	public EarthLikeArea(double x, double y, int w, int h, int n, 
			int pole, double mp, Random r)
	{
		xStart = x;
		yStart = y;
		width = w;
		height = h;
		nodeNumber = n;
		poles = pole;
		minPull = mp;
		currentIndex = 0;
		random = r;
		poleXs = new double [poles];
		poleYs = new double [poles];
		for (int a = 0; a < poles; a++) 
		{
			poleXs[a] = random.nextDouble()*width + xStart;
			poleYs[a] = random.nextDouble()*height + yStart;
		}
	}
	
	public boolean canGenerateMoreNodes()
	{
		if(currentIndex < nodeNumber)
			return true;
		else
			return false;
	}
	public NodeWithCoordinates generateNext(int a)
	{
		double x = random.nextDouble()*width + xStart;
		double y = random.nextDouble()*height + yStart;
		
		if (poles > 0) {
			// Pull node towards pole...
			int cp = currentIndex%poles;
			
			double dx = GeometryUtil.wrap(poleXs[cp]-x);
			double dy = GeometryUtil.wrap(poleYs[cp]-y);
			
			// Create a random percentage to draw the node towards the pole
			double chance = random.nextDouble() + minPull;
			if (chance > 1)
				chance = 1;
			if (Math.abs((poleXs[cp] - x + width) % width) > dx)
				x = Math.abs((x - (dx)*chance + width)%width) + xStart;
			else
				x = Math.abs((x + (dx)*chance + width)%width) + xStart;
			if(x > 180)
			{
				x = x - 360;
			}
			//x = (int)(x + ((poleXs[cp] - x + width)%width)*chance);
			//y = (int)(y + (poleYs[cp] - y)*chance);
			if (Math.abs((poleYs[cp] - y + height) % height) > dy)
				y = Math.abs((y - (dy)*chance + height)%height) + yStart;
			else
				y = Math.abs((y + (dy)*chance + height)%height) + yStart;
		}
		currentIndex++;
		return new NodeWithCoordinates(a+1,x,y);
	}
}

public class EarthLikeNetworkTopologyGenerator 
implements NetworkTopologyGenerator
{
	private static final int DefaultWidth = 360;
	private static final int DefaultHeight = 180;
	
	private Random random;
	private int nodes;
	private int poles;
	private int width;
	private int height;
	private double minPull;
	
	
	/** Mandatory no-argument constructor. */
	public EarthLikeNetworkTopologyGenerator () {/**/}

	/**
	 * This takes the random number generator from the system settings, and the
	 * parameters nodes (required), poles (optional), world-width (optional, 
	 * miles lets say), and world-height (optional, also miles).  
	 */
	public void initalize (
			ExperimentConfiguration systemSettings, Map<String, String> moduleSettings
			)
	throws DseException
	{
		//this.random = systemSettings.getRandom();
		this.random = new Random();
		ModuleUtil.SettingsReader sr = ModuleUtil.getSettingsReader(
				this.getClass(), moduleSettings
				);
		
		nodes = sr.getInteger("nodes");		
		poles = sr.getOptionalInteger("poles", 0);
		width = sr.getOptionalInteger("world-width", DefaultHeight);
		height = sr.getOptionalInteger("world-height", DefaultWidth);
		minPull = sr.getOptionalDouble("min-pull", 0.0);
	}
	
	/**
	 * Generate and return a network.
	 */
	public Network createNetwork () throws DseException {
		EarthLikeNetwork eln = new EarthLikeNetwork (width, height, nodes);
	
		//NA, EU, AS, AF, AU, SA
		//double[][] areas = {{-125, 20, 55, 40, 0.25}, {-5, 30, 40, 25, 0.2}, 
		//		{35, 10, 110, 55, 0.3}, {-5, -30, 50, 55, 0.1}, 
		//		{120, -35, 35, 20, 0.05}, {-65, -45, 35, 50, 0.1}
		//};
		
		double[][] areas = {{-125, 20, 55, 40, 0.4}, {-5, 30, 40, 25, 0.2}, 
				{35, 10, 110, 55, 0.15}, {-5, -30, 50, 55, 0.1}, 
				{120, -35, 35, 20, 0.05}, {-65, -45, 35, 50, 0.1}
		};
		//double[][] areas = {{-125, 20, 55, 40, 1}};
		EarthLikeArea[] ela = new EarthLikeArea[areas.length];
		for(int i=0; i<areas.length; i++)
		{
			ela[i] = new EarthLikeArea(areas[i][0], areas[i][1], (int)areas[i][2], (int)areas[i][3],
					(int)(nodes*areas[i][4]), poles, minPull, random);
		}
		for(int a=0; a<nodes; a++)
		{
			/*double xStart = areas[i][0];
			double yStart = areas[i][1];
			int width = (int)areas[i][2];
			int height = (int)areas[i][3];
			int areaNodes = nodeID + (int)(nodes*areas[i][4]);
			                        
			double [] poleXs = new double [poles];
			double [] poleYs = new double [poles];

			for (int a = 0; a < poles; a++) {
				poleXs[a] = random.nextDouble()*width + xStart;
				poleYs[a] = random.nextDouble()*height + yStart;
			}
			for (int a = nodeID; a < areaNodes; a++) {
				double x = random.nextDouble()*width + xStart;
				double y = random.nextDouble()*height + yStart;
				
				if (poles > 0) {
					// Pull node towards pole...
					int cp = a%poles;
					
					double dx = GeometryUtil.wrap(poleXs[cp]-x);
					double dy = GeometryUtil.wrap(poleYs[cp]-y);
					
					// Create a random percentage to draw the node towards the pole
					double chance = random.nextDouble() + minPull;
					if (chance > 1)
						chance = 1;
					if (Math.abs((poleXs[cp] - x + width) % width) > dx)
						x = Math.abs((x - (dx)*chance + width)%width) + xStart;
					else
						x = Math.abs((x + (dx)*chance + width)%width) + xStart;
					if(x > 180)
					{
						x = x - 360;
					}
					//x = (int)(x + ((poleXs[cp] - x + width)%width)*chance);
					//y = (int)(y + (poleYs[cp] - y)*chance);
					if (Math.abs((poleYs[cp] - y + height) % height) > dy)
						y = Math.abs((y - (dy)*chance + height)%height) + yStart;
					else
						y = Math.abs((y + (dy)*chance + height)%height) + yStart;
				}*/
			int areaIndex = -1;
			//int[] areaProb = {0, 25, 45, 75, 85, 90, 100};
			int[] areaProb = {0, 40, 60, 75, 85, 90, 100};
			while(areaIndex == -1)
			{
				int rand = random.nextInt(100);
				for(int i=1; i<areaProb.length; i++)
				{
					if((rand>=areaProb[i-1]) && (rand<areaProb[i]) && ela[i-1].canGenerateMoreNodes())
					{
						areaIndex = i-1;
						break;
					}
				}
			}
			eln.addNode(ela[areaIndex].generateNext(a));
		}
		
		return eln;
	}

	
	
	/**
	 * For testing :)
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main (String [] args) throws Exception {
		if (args.length != 4 && args.length != 5) {
			System.out.println(
					"Usage: java -cp ... " +
					"dse.modules.eln.EarthLikeNetworkTopologyGenerator " +
					"<nodes> <poles> <min-pull> <output-file> (seed)"
					);
			System.exit(-1);
		}
		
		ExperimentConfiguration ss = TestHelper.createSystemSettings(args, 4);
		EarthLikeNetwork network = TestHelper.createELNetwork(
				ss, args, 0, 1, 2);
		System.out.println(network.nodeCount());
		
		System.out.println("Creating image...");
		EarthLikeNetworkImagingUtil.drawNetwork(network, args[3]);
		
		System.out.println("Done.");
	}
	
}